package jmine.tec.report.xls;

import java.io.IOException;
import java.io.OutputStream;

import jmine.tec.report.api.Report;
import jmine.tec.report.api.ReportElement;
import jmine.tec.report.api.table.ReportCell;
import jmine.tec.report.export.ReportFileExporter;
import jmine.tec.utils.Tuple;

import org.apache.commons.lang.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFPalette;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;

/**
 * Exportador de relatório para arquivo XLS
 * 
 * @author lundberg
 */
public class ReportXLSExporter implements ReportFileExporter {

    public static final int MAX_SHEET_NAME_LENGTH = 31;

    public static final String XLS_HEADER_RENDER_SHEET = "xlsHeaderRenderSheet";

    public static final String XLS_HEADER_SHEET_NAME = "xlsHeaderSheetName";

    public static final String XLS_HEADER_PARAMETERS_PER_ROW = "xlsHeaderParameterPerRow";

    private static final byte GRAY_80_PERCENT_COLOR_BLUE = (byte) 161;

    private static final byte GRAY_80_PERCENT_COLOR_GREEN = (byte) 161;

    private static final byte GRAY_80_PERCENT_COLOR_RED = (byte) 161;

    private static final byte GRAY_25_PERCENT_COLOR_BLUE = (byte) 239;

    private static final byte GRAY_25_PERCENT_COLOR_GREEN = (byte) 239;

    private static final byte GRAY_25_PERCENT_COLOR_RED = (byte) 239;

    private static final byte PALE_BLUE_COLOR_BLUE = (byte) 225;

    private static final byte PALE_BLUE_COLOR_GREEN = (byte) 198;

    private static final byte PALE_BLUE_COLOR_RED = (byte) 171;

    private static final String RENDERER_TYPE = "xls";

    private String logoPath;

    private ReportRendererXLSRegistry reportRendererRegistry;

    /**
     * {@inheritDoc}
     */
    public void export(OutputStream outputStream, Report report) throws IOException {
        HSSFWorkbook workbook = new HSSFWorkbook();

        // Configurando as cores da planilha
        HSSFPalette palette = workbook.getCustomPalette();
        palette.setColorAtIndex(HSSFColor.GREY_80_PERCENT.index, GRAY_80_PERCENT_COLOR_RED, GRAY_80_PERCENT_COLOR_GREEN,
                GRAY_80_PERCENT_COLOR_BLUE);
        palette.setColorAtIndex(HSSFColor.GREY_25_PERCENT.index, GRAY_25_PERCENT_COLOR_RED, GRAY_25_PERCENT_COLOR_GREEN,
                GRAY_25_PERCENT_COLOR_BLUE);
        palette.setColorAtIndex(HSSFColor.PALE_BLUE.index, PALE_BLUE_COLOR_RED, PALE_BLUE_COLOR_GREEN, PALE_BLUE_COLOR_BLUE);

        this.renderHeader(workbook, report);

        for (ReportElement element : report.getElements()) {
            ReportElementXLSRenderer renderer = this.reportRendererRegistry.getRenderer(element.getRendererType());
            if (renderer.shouldRender()) {
                String sheetName = element.getReportId();
                sheetName = sheetName.replaceAll("[\\[\\]*/\\\\?:]", ""); // Caracteres [ ] * / \ ? : não podem ser utilizados
                sheetName = StringUtils.abbreviate(sheetName, MAX_SHEET_NAME_LENGTH);
                HSSFSheet sheet = workbook.createSheet(sheetName);
                renderer.render(workbook, sheet, element);
            }
        }

        workbook.write(outputStream);
    }

    /**
     * Cria o cabeçalho
     * 
     * @param workbook workbook
     * @param report report
     */
    private void renderHeader(HSSFWorkbook workbook, Report report) {
        if (report.getHeader().getParameters().isEmpty()) {
            return;
        }
        if (!report.getParameters().getBoolean(XLS_HEADER_RENDER_SHEET, false)) {
            return;
        }
        int parametersPerRow = report.getParameters().getInteger(XLS_HEADER_PARAMETERS_PER_ROW, 1);
        HSSFCellStyle labelStyle = this.createParameterLabelCellStyle(workbook);
        HSSFCellStyle valueStyle = this.createParameterValueCellStyle(workbook);
        HSSFSheet sheet = workbook.createSheet(getSheetName(report));
        HSSFRow row = sheet.createRow(sheet.getLastRowNum());
        int column = 0;
        for (ReportCell cell : report.getHeader().getParameters()) {
            if (row == null || column == parametersPerRow) {
                row = sheet.createRow(sheet.getLastRowNum() + 1);
                column = 0;
            }
            Tuple<?, ?> parameter = (Tuple<?, ?>) cell.getValue();
            String label = parameter.head() == null ? "" : parameter.head().toString();
            String value = parameter.tail() == null ? "" : parameter.tail().toString();
            this.renderCell(workbook, row, labelStyle, column, label);
            this.renderCell(workbook, row, valueStyle, column + 1, value);
            column++;
        }
    }

    /**
     * Get sheet name
     * 
     * @param report report
     * @return sheet name
     */
    private String getSheetName(Report report) {
        return report.getParameters().getString(XLS_HEADER_SHEET_NAME) != null ? report.getParameters().getString(XLS_HEADER_SHEET_NAME)
                : "Parâmetros";
    }

    /**
     * Aplica a célula à linha, na coluna dada.
     * 
     * @param workbook workbook
     * @param row row
     * @param style style
     * @param column column
     * @param value value
     */
    private void renderCell(HSSFWorkbook workbook, HSSFRow row, HSSFCellStyle style, int column, String value) {
        HSSFCell hssfCell = this.getCell(row, style, column);
        hssfCell.setCellValue(new HSSFRichTextString(value));
    }

    /**
     * Get a specific cell from a row. If the cell doesn't exist, then create it.
     * 
     * @param row The row that the cell is part of
     * @param style style
     * @param column The column index that the cell is in.
     * @return The cell indicated by the column.
     */
    private HSSFCell getCell(HSSFRow row, HSSFCellStyle style, int column) {
        HSSFCell cell = row.getCell(column, HSSFRow.RETURN_NULL_AND_BLANK);
        if (cell == null) {
            cell = row.createCell(column);
        }
        cell.setCellStyle(style);
        return cell;
    }

    /**
     * Cria um novo estilo de celula que deve ser utilizado nas celulas de titulo (service name, service action).
     * 
     * @param workbook workbook
     * @return estilo de celula
     */
    private HSSFCellStyle createParameterLabelCellStyle(HSSFWorkbook workbook) {
        HSSFCellStyle style = workbook.createCellStyle();

        // Bordas
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style.setBottomBorderColor(HSSFColor.BLACK.index);
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style.setLeftBorderColor(HSSFColor.BLACK.index);
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style.setRightBorderColor(HSSFColor.BLACK.index);
        style.setBorderTop(HSSFCellStyle.BORDER_THIN);
        style.setTopBorderColor(HSSFColor.BLACK.index);

        // Cor de fundo
        style.setFillForegroundColor(HSSFColor.GREY_80_PERCENT.index);
        style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);

        // Fonte
        HSSFFont font = workbook.createFont();
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        font.setColor(HSSFColor.WHITE.index);
        style.setFont(font);

        return style;
    }

    /**
     * Cria um novo estilo de celula que deve ser utilizado nas celulas de titulo (service name, service action).
     * 
     * @param workbook workbook
     * @return estilo de celula
     */
    private HSSFCellStyle createParameterValueCellStyle(HSSFWorkbook workbook) {
        HSSFCellStyle style = workbook.createCellStyle();

        // Bordas
        style.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        style.setBottomBorderColor(HSSFColor.BLACK.index);
        style.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        style.setLeftBorderColor(HSSFColor.BLACK.index);
        style.setBorderRight(HSSFCellStyle.BORDER_THIN);
        style.setRightBorderColor(HSSFColor.BLACK.index);
        style.setBorderTop(HSSFCellStyle.BORDER_THIN);
        style.setTopBorderColor(HSSFColor.BLACK.index);

        // Cor de fundo
        style.setFillForegroundColor(HSSFColor.GREY_25_PERCENT.index);
        style.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);

        // Fonte
        HSSFFont font = workbook.createFont();
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        font.setColor(HSSFColor.BLACK.index);
        style.setFont(font);

        return style;
    }

    /**
     * {@inheritDoc}
     */
    public String getRendererType() {
        return RENDERER_TYPE;
    }

    /**
     * {@inheritDoc}
     */
    public String getLogoPath() {
        return this.logoPath;
    }

    /**
     * @param logoPath the logoPath to set
     */
    public void setLogoPath(String logoPath) {
        this.logoPath = logoPath;
    }

    /**
     * @return the reportRendererRegistry
     */
    public ReportRendererXLSRegistry getReportRendererRegistry() {
        return this.reportRendererRegistry;
    }

    /**
     * @param reportRendererRegistry the reportRendererRegistry to set
     */
    public void setReportRendererRegistry(ReportRendererXLSRegistry reportRendererRegistry) {
        this.reportRendererRegistry = reportRendererRegistry;
    }

    /**
     * {@inheritDoc}
     */
    public String getContentType() {
        return "application/vnd.ms-excel";
    }

    /**
     * {@inheritDoc}
     * 
     * @see jmine.tec.report.export.ReportFileExporter#getFileSuffix()
     */
    public String getFileSuffix() {
        return ".xls";
    }
}
